package aden.HexEnv;

import aden.Generic.Gen2DView;
import aden.Generic.GenMain;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.TextView;

public class HexEnvView extends Gen2DView
{
    public HexEnvWorld GetHexEnvWorld() {return (HexEnvWorld)(mMain.GetWorld());}
 
 
 
 class HexEnvViewThread extends Thread {


     private SurfaceHolder mSurfaceHolder;
     private Context mContext;
     private Handler mHandler;

     private HexEnvView mView;
     
     private boolean mRun = false;
     
     public HexEnvViewThread(SurfaceHolder surfaceHolder, 
             HexEnvView inView,
             Context       context,
             Handler       handler) 
     {
         mView = inView;
         // get handles to some important objects
         mSurfaceHolder = surfaceHolder;
         mHandler = handler;
         mContext = context;

         Resources res = context.getResources();


     }

     @Override
     public void run() {
         while (mRun) {
             if (mIsRedraw)
             {
             Canvas c = null;
             try {
                 c = mSurfaceHolder.lockCanvas(null);
                 synchronized (mSurfaceHolder) {
                     doDraw(c);
                 }
             } finally {
                 // do this in a finally so that if an exception is thrown
                 // during the above, we don't leave the Surface in an
                 // inconsistent state
                 if (c != null) {
                     mSurfaceHolder.unlockCanvasAndPost(c);
                 }
             }
             }
         }
     }

     /**
      * Restores game state from the indicated Bundle. Typically called when
      * the Activity is being restored after having been previously
      * destroyed.
      * 
      * @param savedState Bundle containing the game state
      */
     public synchronized void restoreState(Bundle savedState) {
         //           synchronized (mSurfaceHolder) {
         //               setState(STATE_PAUSE);
         //               mDifficulty = savedState.getInt(KEY_DIFFICULTY);
         //               mX = savedState.getDouble(KEY_X);
         //           }
     }

     public Bundle saveState(Bundle map) {
         //           synchronized (mSurfaceHolder) {
         //               if (map != null) {
         //                   map.putInt(KEY_DIFFICULTY, Integer.valueOf(mDifficulty));
         //                   map.putDouble(KEY_X, Double.valueOf(mX));
         //               }
         //           }
                    return map;
     }

     public void setRunning(boolean b) {
         mRun = b;
     }

     /**
      * Sets the game mode. That is, whether we are running, paused, in the
      * failure state, in the victory state, etc.
      * 
      * @see #setState(int, CharSequence)
      * @param mode one of the STATE_* constants
      */
     public void setState(int mode) {
         //           synchronized (mSurfaceHolder) {
         //               setState(mode, null);
         //           }
     }

     /**
      * Sets the game mode. That is, whether we are running, paused, in the
      * failure state, in the victory state, etc.
      * 
      * @param mode one of the STATE_* constants
      * @param message string to add to screen or null
      */
     public void setState(int mode, CharSequence message) {

     }

     /* Callback invoked when the surface dimensions change. */
     public void setSurfaceSize(int width, int height) {
         // synchronized to make sure these all change atomically
         synchronized (mSurfaceHolder) {
             mCanvasWidth = width;
             mCanvasHeight = height;
             
             mEnvBitmap = Bitmap.createBitmap(mCanvasWidth,mCanvasHeight,Bitmap.Config.ARGB_8888);
             mAddBitmap = Bitmap.createBitmap(mCanvasWidth,mCanvasHeight,Bitmap.Config.ARGB_8888);
             RefreshEnv();
	     FlushRefresh();
         }
     }

     /**
      * Resumes from a pause.
      */
     public void unpause() {
         // Move the real time clock up to now
         //           synchronized (mSurfaceHolder) {
         //               mLastTime = System.currentTimeMillis() + 100;
         //           }
         //           setState(STATE_RUNNING);
     }


     private void doDraw(Canvas canvas) {

         if (mIsRedraw)
         {
             canvas.drawBitmap(mEnvBitmap, 0, 0, mLinePaint);
             canvas.drawBitmap(mAddBitmap, 0, 0, mLinePaint);
             mIsRedraw = false;
         }
     }



     
     
 }
 /** The thread that actually draws the animation */
 private HexEnvViewThread thread;

 
 /** Paint to draw the lines on screen. */
 private Paint mLinePaint;
 protected Paint mClearingPaint;
 
 // about the view
 protected int mCenterPosX;
 protected int mCenterPosY;
 
 // distance in pixel between two centers of horizontally adjacent tiles.
 protected float mGrid;

 private boolean mIsGridDisplayed;
 
 static protected float sHexPtX[] = null;
 static protected float sHexPtY[] = null;
 protected HexEnvWorld.HexEnvIterTile mIterTile;

 static protected final float COS30 = (float) 0.866025;
 static protected final float SIN30 = (float) 0.5;
 
 protected RectF mTmpRect;

 private boolean mIsSelectionRefreshed = false;

 protected int mCanvasHeight = 1;
 protected int mCanvasWidth = 1;
 protected boolean mIsRedraw = false;
 
 protected Bitmap mEnvBitmap;
 protected Bitmap mAddBitmap;


 public float mCursorX;
 public float mCursorY;
  
 protected int AdaptToRangeX(int inX)
 {
     if (inX < 0)
     {
         return 0;
     }
     else if (mWorld.GetWidth() <= inX)
     {
         return mWorld.GetWidth()-1;
     }
     return inX;
 }

 protected int AdaptToRangeY(int inY)
 {
     if (inY < 0)
     {
         return 0;
     }
     else if (mWorld.GetHeight() <= inY)
     {
         return mWorld.GetHeight()-1;
     }
     return inY;
 }


 public void Pan(float inPosX,float inPosY)
 {
     mCenterPosX = GetWorldX(inPosX,inPosY);
     mCenterPosY = GetWorldY(inPosX,inPosY);
     RefreshEnv();
     FlushRefresh();
 }
 
 public void Zoom(float inPosX1,float inPosY1,float inPosX2,float inPosY2)
 {
     if (inPosX1 < inPosX2)
     {
         int thePosX1 = GetWorldX(inPosX1,inPosY1);
         int thePosY1 = GetWorldY(inPosX1,inPosY1);
         int thePosX2 = GetWorldX(inPosX2,inPosY2);
         int thePosY2 = GetWorldY(inPosX2,inPosY2);
         
         mCenterPosX = (thePosX1 + thePosX2) / 2;
         mCenterPosY = (thePosY1 + thePosY2) / 2;
         mGrid = thePosX2 - thePosX1;
         if (mGrid == 0)
         {
        	 mGrid = mCanvasWidth;
         }
         else
         {
        	 mGrid = mCanvasWidth / mGrid;
         }
     }
     else
     {
         // ## a faire
    	 mGrid = mGrid / 2;
    	 
    	 if (mGrid < 10.0)
    	 {
    		 mGrid = 10;
    	 }
     }
     RefreshEnv();
     FlushRefresh();
 }

 
 // approximatif
 public int GetWorldX(float inX,
                      float inY)
 {
     int theRealCenterX = mCanvasWidth / 2;
     int theRealCenterY = mCanvasHeight / 2;

     // on determine le candidate le plus proche  par rapport au centre
     
     int theResultY = mCenterPosY + Math.round((inY - theRealCenterY) / (mGrid * COS30));
     float theX = (inX - theRealCenterX);
     if (theResultY % 2 != 0)
     {
         theX -= mGrid / 2;
     }
     int theResultX = mCenterPosX + Math.round(theX / mGrid);
     
     return theResultX;
 }

 public int GetWorldY(float inX,
                      float inY)
 {
     int theRealCenterY = mCanvasHeight / 2;
     
     // on determine le candidate le plus proche  par rapport au centre

     return  mCenterPosY + Math.round((inY - theRealCenterY) / (mGrid * COS30));

 }

public void FlushRefresh()
{
    mIsRedraw = true;
}
 
 protected synchronized boolean  RefreshAdd()
 {
     if (mWorld == null)
     {
         return false;
     }
     mAddBitmap.eraseColor(Color.TRANSPARENT);

     // ## a specialiser
     
     return true;
 }

public float GetTileCenterX(int inPosX,int inPosY)
{
	return GetHexCenterX(inPosX,inPosY,mCanvasWidth,mGrid,mCenterPosX); 
}
public float GetTileCenterY(int inPosX,int inPosY)
{
	return GetHexCenterY(inPosX,inPosY,mCanvasHeight,mGrid,mCenterPosY); 
}

 
static public float GetHexCenterX(int inPosX,int inPosY,
		                    int inCanvasWidth, float inGrid,int inCenterPosX)
{
	float theResult =  inCanvasWidth / 2 + (inPosX - inCenterPosX) * inGrid;
    if (inPosY % 2 != 0)
    {
    	theResult += inGrid / 2;
    }
    return theResult;
}

static public float GetHexCenterY(int inPosX,int inPosY,
                            int inCanvasHeight, float inGrid,int inCenterPosY)
{
	return inCanvasHeight / 2 + (inPosY - inCenterPosY) * inGrid * COS30; // SIN60
}
 
static public float GetHexTileOffsetX(int inNeighbour,float inGrid)
{
    return sHexPtX[inNeighbour]* inGrid / (2 * COS30);
}

static public float GetHexTileOffsetY(int inNeighbour,float inGrid)
{
    return sHexPtY[inNeighbour]*inGrid / (2 * COS30);	
}


protected Path CalculateCellPath(float inX,float inY,float inHexRadius)
{
	Path theShadePath = new Path();
	theShadePath.moveTo(inX + sHexPtX[0]*inHexRadius,
			inY + sHexPtY[0]*inHexRadius);
	for (int i = 1;
			i < 6;
			++i)
	{
		theShadePath.lineTo(inX + sHexPtX[i]*inHexRadius,
				inY + sHexPtY[i]*inHexRadius);
	}
	theShadePath.close();
	return theShadePath;
}


 protected synchronized boolean RefreshEnv()
 {
     if (mWorld == null)
     {
         return false;
     }
     mEnvBitmap.eraseColor(Color.BLACK);
     
     int thePosXMin = AdaptToRangeX(mCenterPosX - (int)(mCanvasWidth / (mGrid *2)) - 1);
     int thePosYMin = AdaptToRangeY(mCenterPosY - (int)(mCanvasHeight / (mGrid *2)) - 1);
     int thePosXMax = AdaptToRangeX(mCenterPosX + (int)(mCanvasWidth / (mGrid *2)) + 1);
     int thePosYMax = AdaptToRangeY(mCenterPosY + (int)(mCanvasHeight / (mGrid *2)) + 1);
     float theHexRadius = mGrid / (2 * COS30);

     Canvas theEnvCanvas = new Canvas(mEnvBitmap);
     // on dessine le fond
     //          canvas.drawBitmap(mBackgroundImage, 0, 0, null);
     mTmpRect.set(0,0,
             mCanvasWidth,mCanvasHeight);
     theEnvCanvas.drawRect(mTmpRect,mClearingPaint);

     float[] thePtTable = new float[6 * 4];

     for (mIterTile.Begin(mWorld,
             thePosXMin,
             thePosYMin,
             thePosXMax,
             thePosYMax);
             mIterTile.IsNotDone();
             mIterTile.Next())
     {               
         float theCurrentPosX = GetTileCenterX(mIterTile.GetX(),mIterTile.GetY());  
         float theCurrentPosY = GetTileCenterY(mIterTile.GetX(),mIterTile.GetY());  

         // tiles grid rendering         
         if (mIsGridDisplayed)
         {
             for (int i = 0;
                     i < 6;
                     ++i)
             {
                 thePtTable[i * 4 + 0] = theCurrentPosX + GetHexTileOffsetX(i,mGrid);
                 thePtTable[i * 4 + 1] = theCurrentPosY + GetHexTileOffsetY(i,mGrid);
                 thePtTable[i * 4 + 2] = theCurrentPosX + GetHexTileOffsetX((i+1) % 6,mGrid);
                 thePtTable[i * 4 + 3] = theCurrentPosY + GetHexTileOffsetY((i+1) % 6,mGrid);
             }           

             theEnvCanvas.drawLines(thePtTable,mLinePaint);
         }


         // tiles with life:

             // ## a faire
     }
     
     RefreshAdd();

     return true;
 }

 
 SurfaceHolder mHolder;
 Context mContext;
 public HexEnvView(Context context, AttributeSet attrs) {
     super(context, attrs);

     // register our interest in hearing about changes to our surface
     mHolder = getHolder();
     mContext = context;
     mHolder.addCallback(this);
     
     mWorld = null;

     if (sHexPtX == null)
     {
    	 sHexPtX = new float[6];
    	 sHexPtY = new float[6];
    	 sHexPtX[0] = COS30;
    	 sHexPtY[0] = SIN30;
    	 sHexPtX[1] = 0;
    	 sHexPtY[1] = 1;
    	 sHexPtX[2] = -COS30;
    	 sHexPtY[2] = SIN30;
    	 sHexPtX[3] = -COS30;
    	 sHexPtY[3] = -SIN30;
    	 sHexPtX[4] = 0;
    	 sHexPtY[4] = -1;
    	 sHexPtX[5] = COS30;
    	 sHexPtY[5] = -SIN30;
     }
     // create thread only; it's started in surfaceCreated()
     thread = new HexEnvViewThread(mHolder, this,mContext, new Handler() {
    	 @Override
    	 public void handleMessage(Message m) {
//             mStatusText.setVisibility(m.getData().getInt("viz"));
//             mStatusText.setText(m.getData().getString("text"));
         }
     });

     setFocusable(true); // make sure we get key events
 }

 protected HexEnvWorld mWorld;
 
public boolean Init(GenMain inMain)
 {
    super.Init(inMain);
    mWorld = GetHexEnvWorld();
    mTmpRect = new RectF(0,0,0,0);
    
    mIsGridDisplayed = false;
        
    mIterTile = mWorld.new HexEnvIterTile();
    
    mLinePaint = new Paint();
    mLinePaint.setAntiAlias(true);
    mLinePaint.setARGB(255, 255, 255, 255);
    
    mClearingPaint = new Paint();
    mClearingPaint.setAntiAlias(true);
    mClearingPaint.setARGB(255, 0,0,0);
    
    
    mEnvBitmap = Bitmap.createBitmap(mCanvasWidth,mCanvasHeight,Bitmap.Config.ARGB_8888);
    mAddBitmap = Bitmap.createBitmap(mCanvasWidth,mCanvasHeight,Bitmap.Config.ARGB_8888);

    // cache handles to our key sprites & other drawables
    //           mLanderImage = context.getResources().getDrawable(
    //                   R.drawable.lander_plain);

//    mBackgroundImage = BitmapFactory.decodeResource(res,
//            R.drawable.earthrise);
    
    
    setOnTouchListener(this);


//    RefreshEnv();
    return true;
 }
 
 /**
  * Fetches the animation thread corresponding to this LunarView.
  * 
  * @return the animation thread
  */
 public HexEnvViewThread getThread() {
     return thread;
 }

 /**
  * Standard override to get key-press events.
  */
 @Override
 public boolean onKeyDown(int keyCode, KeyEvent msg) {
//     return thread.doKeyDown(keyCode, msg);
     return true;
 }

 /**
  * Standard override for key-up. We actually care about these, so we can
  * turn off the engine or stop rotating.
  */
 @Override
 public boolean onKeyUp(int keyCode, KeyEvent msg) {
//     return thread.doKeyUp(keyCode, msg);
     return true;
 }

 /**
  * Standard window-focus override. Notice focus lost so we can pause on
  * focus lost. e.g. user switches to take a call.
  */
 @Override
 public void onWindowFocusChanged(boolean hasWindowFocus) {
//     if (!hasWindowFocus) thread.pause();
 }

 /**
  * Installs a pointer to the text view used for messages.
  */
 public void setTextView(TextView textView) {
//     mStatusText = textView;
 }

 /* Callback invoked when the surface dimensions change. */
 public void surfaceChanged(SurfaceHolder holder, int format, int width,
         int height) {
     thread.setSurfaceSize(width, height);
 }

 /*
  * Callback invoked when the Surface has been created and is ready to be
  * used.
  */
 public void surfaceCreated(SurfaceHolder holder) {
     // start the thread here so that we don't busy-wait in run()
     // waiting for the surface to be created
     thread.setRunning(true);
     thread.start();
 }

 /*
  * Callback invoked when the Surface has been destroyed and must no longer
  * be touched. WARNING: after this method returns, the Surface/Canvas must
  * never be touched again!
  */
 public void surfaceDestroyed(SurfaceHolder holder) {
     // we have to tell thread to shut down & wait for it to finish, or else
     // it might touch the Surface after we return and explode
     boolean retry = true;
     thread.setRunning(false);
     while (retry) {
         try {
             thread.join();
             retry = false;
         } catch (InterruptedException e) {
         }
     }
 }
}

